﻿////////////////////////////////////////////////////////////////////////////////
//
//  Copyright (C) 2007 Codeiron Ltd.
//  All Rights Reserved.
//
//  VAComboBox is released under the BSD license.
//
//  Redistribution and use in source and binary forms, with or without modification,
//  are permitted provided that the following conditions are met:
//
//  * Redistributions of source code must retain the above copyright notice, this
//    list of conditions and the following disclaimer.
//  * Redistributions in binary form must reproduce the above copyright notice, this
//    list of conditions and the following disclaimer in the documentation and/or
//    other materials provided with the distribution.
//  * Neither the name of the Codeiron Ltd. nor the names of its contributors may be
//    used to endorse or promote products derived from this software without specific
//    prior written permission.
//
//  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
//  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
//  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
//  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
//  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
//  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
//  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
//  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
//  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
//  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
//  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
////////////////////////////////////////////////////////////////////////////////

package com
{
  import mx.controls.ComboBox;
  import mx.collections.IList;
  import mx.collections.XMLListCollection;
  import flash.events.Event;

  /**
  *  The <code>VAComboBox</code> component is a value-aware <code>ComboBox</code>.
  *
  *  <p>You can define a preselected value.</p>
  */
  public class VAComboBox extends ComboBox
  {
    /**
    *  Storage for the candidate value.
    *  @private
    */
    private var candidateValue_:Object = null;

    /**
    *  A flag which indicates whether the value property has changed.
    *  @private
    */
    private var valueChanged_:Boolean = false;

    /**
    *  Storage for the candidate data provider.
    *  @private
    */
    private var candidateDataProvider_:Object = null;

    /**
    *  A flag which indicates whether the dataProvider property has changed.
    *  @private
    */
    private var dataProviderChanged_:Boolean = false;

    /**
    *  A flag which indicates whether the dataField property has changed.
    *  @private
    */
    private var dataFieldChanged_:Boolean = false;

    /**
    *  Storage for the dataField property.
    *  @private
    */
    private var dataField_:String = null;

    /**
     *  The name of the field or property in the data provider item which is used as data.
     */
    [Bindable("dataFieldChanged")]
    [Inspectable(category="Data")]
    public function get dataField():String
    {
      return dataField_;
    }

    /**
    *  @private
    */
    public function set dataField(val:String):void
    {
      dataField_ = val;
      dataFieldChanged_ = true;
      dispatchEvent(new Event("dataFieldChanged"));
      invalidateProperties();
    }

	[Inspectable(category="Data")]
	/**
	*  @private
	*/
    public function set value(val:Object):void
    {
      candidateValue_ = val;
      valueChanged_ = true;
      invalidateProperties();
    }

    /**
    *  The value of the selected item.
    *
    *  <p>If you set the <code>value</code> property, an item is automatically selected, provided one of the following conditions
    *  is true for that item:</p>
    *
    *  <ul>
    *     <li>The <code>dataField</code> property is set and item[dataField]==value is true, or</li>
    *     <li>the <code>labelField</code> property is set and item[labelField]==value is true, or</li>
    *     <li>item==value is true.</li>
    *  </ul>
    *
    *  <p>If you use an XMLListCollection for a dataProvider all properties and items are converted to <code>String</code>
    *  before the comparison.</p>
    */

    override public function get value():Object
    {
      var res:Object = null;
      if(editable)
      {
        res = text;
      }
      var item:Object = selectedItem;

      if(item!=null)
      {
        if(dataField!=null)
        {
          res = item[dataField];
        }
        else
        if(labelField!=null)
        {
          res = item[labelField];
        }
        else
        {
          res = item;
        }
      }
      return res;
    }

    /**
    *  Processes the properties set on the component.
    */
    override protected function commitProperties():void
    {
      super.commitProperties();
      if(dataProviderChanged_)
      {
        super.dataProvider = candidateDataProvider_;
      }

      if(dataProviderChanged_ || valueChanged_ || dataFieldChanged_)
      {
        applyValue();
      }

      // Clean up...
      candidateDataProvider_ = null;
      dataProviderChanged_ = false;
      candidateValue_ = null;
      valueChanged_ = false;
      dataFieldChanged_ = false;
    }

    /**
    *  It selects an item using the <code>value</code> property.
    *  @private
    *
    *  @see #value
    */
    private function applyValue():void
    {
      if((candidateValue_!=null) && (dataProvider!=null))
      {
        var list:IList = null;
        var len:int = 0;
        if(dataProvider is IList)
        {
          list = dataProvider as IList;
          len = list.length;
        }
        else
        {
          // In any other case we hope that it works :) ...
          len = dataProvider.length;
        }
        var str:String = null;
        if(dataProvider is XMLListCollection)
        {
          str = String(candidateValue_);
        }
        var i:int = 0;
        var found:Boolean = false;
        while((!found) && (i<len))
        {
          var item:Object = null;
          if(list!=null)
          {
            item = list.getItemAt(i);
          }
          else
          {
            // In any other case we hope that it works :) ...
            item = dataProvider[i];
          }
          if(item!=null)
          {
            var label:String = labelField!=null ? String(item[labelField]) : null;
            if(item is XML)
            {
              var dataString:String = dataField!=null ? String(item[dataField]) : null;
              if(str==dataString || str==label || str==item.toString())
              {
                selectedIndex = i;
                found = true;
              }
            }
            else
            {
              var data:Object = dataField!=null ? item[dataField] : null;
              if(candidateValue_==data || candidateValue_==label || candidateValue_==item)
              {
                selectedIndex = i;
                found = true;
              }
            }
          }
          i++;
        }
        if(!found)
        {
          selectedIndex = -1;
        }
      }
      else
      {
        selectedIndex = -1;
      }
    }

    /**
    *  The set of items this component displays.
    *
    *  <p>Overridden version of <code>set</code> function of <code>dataProvider</code>.</p>
    */
    override public function set dataProvider(val:Object):void
    {
      candidateDataProvider_ = val;
      dataProviderChanged_ = true;
      invalidateProperties();
    }
  }
}